package com.ntt.benew.dictionary.api;

import android.os.Build;
import android.text.TextUtils;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.ntt.benew.dictionary.constants.GlobalConfig;
import com.ntt.benew.dictionary.entity.XFResultEntity;
import com.ntt.benew.dictionary.utils.HttpUtil;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/**
 * 讯飞OCR识别
 * https://www.xfyun.cn/doc/words/universal_character_recognition/API.html#%E8%BF%94%E5%9B%9E%E7%BB%93%E6%9E%9C
 */
public class XFApiManger {
    public static final String XF_CN = "cn";
    public static final String XF_EN = "en";

    //讯飞OCR识别
    private static final String XF_OCR_REQUEST_URL = "https://api.xf-yun.com/v1/private/sf8e6aca1";
    //讯飞机器翻译
    private static final String XF_TRANS_REQUEST_URL = "https://itrans.xfyun.cn/v2/its";

    private static final class SingletonHolder {
        private static final XFApiManger INSTANCE = new XFApiManger();
    }

    public static XFApiManger getInstance() {
        return SingletonHolder.INSTANCE;
    }

    /**
     * 请求主方法
     *
     * @return 返回服务结果
     * @throws Exception 异常
     */
    public String doRequest(String filePath) throws Exception {
        URL realUrl = new URL(buildRequestUrl());
        URLConnection connection = realUrl.openConnection();
        HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
        httpURLConnection.setDoInput(true);
        httpURLConnection.setDoOutput(true);
        httpURLConnection.setRequestMethod("POST");
        httpURLConnection.setRequestProperty("Content-type", "application/json");
        OutputStream out = httpURLConnection.getOutputStream();
        String params = buildParam(filePath);
        System.out.println("params=>" + params);
        out.write(params.getBytes());
        out.flush();
        InputStream is;
        try {
            is = httpURLConnection.getInputStream();
        } catch (Exception e) {
            is = httpURLConnection.getErrorStream();
            throw new Exception("make request error:" + "code is " + httpURLConnection.getResponseMessage() + readAllBytes(is));
        }
        return readAllBytes(is);
    }

    /**
     * 请求主方法
     *
     * @return 返回服务结果
     * @throws Exception 异常
     */
    public String doRequest(byte[] src) throws Exception {
        URL realUrl = new URL(buildRequestUrl());
        URLConnection connection = realUrl.openConnection();
        HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
        httpURLConnection.setDoInput(true);
        httpURLConnection.setDoOutput(true);
        httpURLConnection.setRequestMethod("POST");
        httpURLConnection.setRequestProperty("Content-type", "application/json");
        OutputStream out = httpURLConnection.getOutputStream();
        String params = buildParam(src);
        System.out.println("params=>" + params);
        out.write(params.getBytes());
        out.flush();
        InputStream is;
        try {
            is = httpURLConnection.getInputStream();
        } catch (Exception e) {
            is = httpURLConnection.getErrorStream();
            throw new Exception("make request error:" + "code is " + httpURLConnection.getResponseMessage() + readAllBytes(is));
        }
        return readAllBytes(is);
    }

    /**
     * 处理请求URL
     * 封装鉴权参数等
     *
     * @return 处理后的URL
     */
    private String buildRequestUrl() {
        URL url;
        // 替换调schema前缀 ，原因是URL库不支持解析包含ws,wss schema的url
        String requestUrl = XF_OCR_REQUEST_URL;
        String httpRequestUrl = requestUrl.replace("ws://", "http://").replace("wss://", "https://");
        try {
            url = new URL(httpRequestUrl);
            //获取当前日期并格式化
            SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
            format.setTimeZone(TimeZone.getTimeZone("GMT"));
            String date = format.format(new Date());
            String host = url.getHost();
            /*if (url.getPort()!=80 && url.getPort() !=443){
                host = host +":"+String.valueOf(url.getPort());
            }*/
            String builder = "host: " + host + "\n" +
                    //
                    "date: " + date + "\n" +
                    //
                    "POST " + url.getPath() + " HTTP/1.1";
            //System.err.println(builder.toString());
            Charset charset = StandardCharsets.UTF_8;
            Mac mac = Mac.getInstance("hmacsha256");
            SecretKeySpec spec = new SecretKeySpec(GlobalConfig.XF_API_SECRET.getBytes(charset), "hmacsha256");
            mac.init(spec);
            byte[] hexDigits = mac.doFinal(builder.getBytes(charset));
            String sha;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                sha = Base64.getEncoder().encodeToString(hexDigits);
            } else {
                sha = android.util.Base64.encodeToString(hexDigits, android.util.Base64.NO_WRAP);
            }
            String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", GlobalConfig.XF_API_KEY, "hmac-sha256", "host date request-line", sha);
            String authBase;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                authBase = Base64.getEncoder().encodeToString(authorization.getBytes(charset));
            } else {
                authBase = android.util.Base64.encodeToString(authorization.getBytes(charset), android.util.Base64.NO_WRAP);
            }
            return String.format("%s?authorization=%s&host=%s&date=%s", requestUrl, URLEncoder.encode(authBase), URLEncoder.encode(host), URLEncoder.encode(date));
        } catch (Exception e) {
            throw new RuntimeException("assemble requestUrl error:" + e.getMessage());
        }
    }

    /**
     * 组装请求参数
     * 直接使用示例参数，
     * 替换部分值
     *
     * @return 参数字符串
     */
    private String buildParam(String filePath) throws IOException {
        String image;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            image = Base64.getEncoder().encodeToString(read(filePath));
        } else {
            image = android.util.Base64.encodeToString(read(filePath), android.util.Base64.NO_WRAP);
        }
        return "{" +
                "    \"header\": {" +
                "        \"app_id\": \"" + GlobalConfig.XF_APP_ID + "\"," +
                "        \"status\": 3" +
                "    }," +
                "    \"parameter\": {" +
                "        \"sf8e6aca1\": {" +
                "            \"category\": \"ch_en_public_cloud\"," +
                "            \"result\": {" +
                "                \"encoding\": \"utf8\"," +
                "                \"compress\": \"raw\"," +
                "                \"format\": \"json\"" +
                "            }" +
                "        }" +
                "    }," +
                "    \"payload\": {" +
                "        \"sf8e6aca1_data_1\": {" +
                "            \"encoding\": \"jpg\"," +
                "            \"status\": " + 3 + "," +
                "            \"image\": \"" + image + "\"" +
                "        }" +
                "    }" +
                "}";
    }


    /**
     * 组装请求参数
     * 直接使用示例参数，
     * 替换部分值
     *
     * @return 参数字符串
     */
    private String buildParam(byte[] src) throws IOException {
        String image;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            image = Base64.getEncoder().encodeToString(src);
        } else {
            image = android.util.Base64.encodeToString(src, android.util.Base64.NO_WRAP);
        }
        return "{" +
                "    \"header\": {" +
                "        \"app_id\": \"" + GlobalConfig.XF_APP_ID + "\"," +
                "        \"status\": 3" +
                "    }," +
                "    \"parameter\": {" +
                "        \"sf8e6aca1\": {" +
                "            \"category\": \"ch_en_public_cloud\"," +
                "            \"result\": {" +
                "                \"encoding\": \"utf8\"," +
                "                \"compress\": \"raw\"," +
                "                \"format\": \"json\"" +
                "            }" +
                "        }" +
                "    }," +
                "    \"payload\": {" +
                "        \"sf8e6aca1_data_1\": {" +
                "            \"encoding\": \"jpg\"," +
                "            \"status\": " + 3 + "," +
                "            \"image\": \"" + image + "\"" +
                "        }" +
                "    }" +
                "}";
    }

    /**
     * 读取流数据
     *
     * @param is 流
     * @return 字符串
     * @throws IOException 异常
     */
    private String readAllBytes(InputStream is) throws IOException {
        byte[] b = new byte[1024];
        StringBuilder sb = new StringBuilder();
        int len = 0;
        while ((len = is.read(b)) != -1) {
            sb.append(new String(b, 0, len, StandardCharsets.UTF_8));
        }
        return sb.toString();
    }

    private static byte[] read(String filePath) throws IOException {
        InputStream in = new FileInputStream(filePath);
        byte[] data = inputStream2ByteArray(in);
        in.close();
        return data;
    }

    private static byte[] inputStream2ByteArray(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 4];
        int n = 0;
        while ((n = in.read(buffer)) != -1) {
            out.write(buffer, 0, n);
        }
        return out.toByteArray();
    }


    /**
     * 讯飞翻译
     *
     * @param word     文本
     * @param fromLang 源语言
     * @param toLang   目标语言
     * @return
     * @throws Exception
     */
    public XFResultEntity doTransRequest(String word, String fromLang, String toLang) throws Exception {
        if (TextUtils.isEmpty(word)) {
            return null;
        }
        String body = buildHttpBody(word, fromLang, toLang);
        Map<String, String> header = buildHttpHeader(body);
        Map<String, Object> resultMap = HttpUtil.doPost2(XF_TRANS_REQUEST_URL, header, body);
        if (resultMap != null && resultMap.containsKey("body") && resultMap.get("body") != null) {
            String data = (String) resultMap.get("body");
            Gson gson = new Gson();
            return gson.fromJson(data, XFResultEntity.class);
        }
        return null;
    }


    /**
     * 组装http请求头
     */
    private Map<String, String> buildHttpHeader(String body) throws Exception {
        Map<String, String> header = new HashMap<>();
        URL url = new URL(XF_TRANS_REQUEST_URL);

        //时间戳
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        Date dateD = new Date();
        String date = format.format(dateD);
        //System.out.println("【ITS WebAPI date】\n" + date);

        //对body进行sha256签名,生成digest头部，POST请求必须对body验证
        String digestBase64 = "SHA-256=" + signBody(body);
        //System.out.println("【ITS WebAPI digestBase64】\n" + digestBase64);

        //hmacsha256加密原始字符串
        StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n").//
                append("date: ").append(date).append("\n").//
                append("POST ").append(url.getPath()).append(" HTTP/1.1").append("\n").//
                append("digest: ").append(digestBase64);
        //System.out.println("【ITS WebAPI builder】\n" + builder);
        String sha = hmacsign(builder.toString(), GlobalConfig.XF_API_SECRET);
        //System.out.println("【ITS WebAPI sha】\n" + sha);

        //组装authorization
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", GlobalConfig.XF_API_KEY, "hmac-sha256", "host date request-line digest", sha);
        System.out.println("【ITS WebAPI authorization】\n" + authorization);

        header.put("Authorization", authorization);
        header.put("Content-Type", "application/json");
        header.put("Accept", "application/json,version=1.0");
        header.put("Host", url.getHost());
        header.put("Date", date);
        header.put("Digest", digestBase64);
        System.out.println("【ITS WebAPI header】\n" + header);
        return header;
    }


    /**
     * 组装http请求体
     */
    private String buildHttpBody(String word, String fromLang, String toLang) throws Exception {
        JsonObject body = new JsonObject();
        JsonObject business = new JsonObject();
        JsonObject common = new JsonObject();
        JsonObject data = new JsonObject();
        //填充common
        common.addProperty("app_id", GlobalConfig.XF_APP_ID);
        //填充business
        business.addProperty("from", fromLang);
        business.addProperty("to", toLang);
        //填充data
        //System.out.println("【OTS WebAPI TEXT字个数：】\n" + TEXT.length());
        byte[] textByte = word.getBytes(StandardCharsets.UTF_8);
        String textBase64;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            textBase64 = Base64.getEncoder().encodeToString(textByte);
        } else {
            textBase64 = android.util.Base64.encodeToString(textByte, android.util.Base64.NO_WRAP);
        }
        //System.out.println("【OTS WebAPI textBase64编码后长度：】\n" + textBase64.length());
        data.addProperty("text", textBase64);
        //填充body
        body.add("common", common);
        body.add("business", business);
        body.add("data", data);
        return body.toString();
    }


    /**
     * hmacsha256加密
     */
    private String hmacsign(String signature, String apiSecret) throws Exception {
        Charset charset = StandardCharsets.UTF_8;
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(signature.getBytes(charset));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            return Base64.getEncoder().encodeToString(hexDigits);
        } else {
            return android.util.Base64.encodeToString(hexDigits, android.util.Base64.NO_WRAP);
        }
    }


    /**
     * 对body进行SHA-256加密
     */
    private String signBody(String body) throws Exception {
        MessageDigest messageDigest;
        String encodestr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(body.getBytes(StandardCharsets.UTF_8));
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                encodestr = Base64.getEncoder().encodeToString(messageDigest.digest());
            } else {
                encodestr = android.util.Base64.encodeToString(messageDigest.digest(), android.util.Base64.NO_WRAP);
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return encodestr;
    }
}
